import { Selection, BaseType } from 'd3-selection';
import L, { LatLng, Point } from 'leaflet';
import { FeatureInstance, FeatureOption } from '../types';
import Feature from './feature';
import { line } from 'd3-shape';
import Vertex from './vertex';
import { deepClone } from '../util/util';
import { getAnotation } from '../util/util';

/**
 * 线路径需要绑定的数据类型
 */
type PathData = {
    className: string;
    latLngs: LatLng[][];
    nodeIds: string[][];
};

class Line extends Feature {
    // @ts-ignore
    data: PathData[] | undefined;
    classNames: string[] = ['shadow', 'stroke'];
    dashedLatlngs: LatLng[][] = []; // 末段虚线，拖动时可能会有多段虚线，所以是二维数组
    dashedVertex?: Vertex;
    relateNodeIds: string[][] = [[]]; // 按照顶点顺序排列
    drawing: boolean = false; // 是否正在绘制

    constructor(id: string, featureOption: FeatureOption, latLngs: LatLng[][], properties?: any) {
        super(id, featureOption, latLngs, 'line', properties);
        if (latLngs.length === 1) {
            this.dashedLatlngs = [[latLngs[0][0], latLngs[0][0]]];
        }
    }

    initData() {
        const { clipPath } = this.featureOption;

        let clipLatLngs = clipPath(this.latLngs);
        if (!clipLatLngs || clipLatLngs.length === 0) {
            // 裁切后没有可显示区域
            this.data = undefined;
        } else {
            // clipNodeIds：判断dragIndexs使用，第一次加载，relateNodeIds不存在，clipNodeIds全是undefined
            const clipNodeIds: string[][] = [[]];
            clipLatLngs[0].forEach((latLng: LatLng) => {
                const existIndex = this.latLngs[0].findIndex((item) => latLng.equals(item));
                if (existIndex !== -1) {
                    clipNodeIds[0].push(this.relateNodeIds[0] ? this.relateNodeIds[0][existIndex] : '');
                } else {
                    clipNodeIds[0].push('');
                }
            });

            this.data = this.classNames.map((className) => {
                return {
                    className,
                    latLngs: clipLatLngs,
                    nodeIds: clipNodeIds,
                };
            });
        }
    }

    add(groupSelection: Selection<SVGGElement | BaseType, Feature, BaseType, string>): Feature {
        this.group = groupSelection;

        this.redraw();
        this.addEventListener();
        return this;
    }

    redraw() {
        this.initData();
        // 会清除虚线
        this.group.selectChildren('path.line').remove();

        if (this.data) {
            this.group
                .selectChildren('path.line')
                .data(this.data)
                .enter()
                .append('path')
                .attr('class', (d) => {
                    return this.type + ' ' + d.className;
                })
                .call(this.drawPath.bind(this));
        }

        return this;
    }

    drawPath(selection: Selection<SVGPathElement, PathData, BaseType, Feature>) {
        const mode = this.featureOption.mode;

        selection.attr('d', (d) => {
            const pixes = d.latLngs[0].map((latLng) => {
                return this.featureOption.transform.project(latLng);
            });
            const dragIndexs = d.nodeIds[0]
                .map((id, index) => {
                    return mode.getType() === 'drag' && id === mode.selectedFeature?.id ? index : undefined;
                })
                .filter((item) => item !== undefined);

            return line().defined((d, i, data) => {
                if (dragIndexs.length === 0) {
                    return true;
                } else {
                    return !dragIndexs.includes(i);
                }
            })(pixes);
        });
    }

    /**
     * 虚线
     * @returns
     */
    redrawDashedLine() {
        this.group.select('path.dashed-line').remove();
        this.dashedLatlngs.forEach((latLngs: LatLng[]) => {
            if (latLngs.length < 2) {
                return;
            }
            const clipLatLngs = this.featureOption.clipPath([latLngs]);
            const pixes = clipLatLngs[0].map((latLng) => {
                return this.featureOption.transform.project(latLng);
            });
            const d = line()(pixes);

            this.group
                .append('path')
                .attr('class', () => {
                    return this.type + ' dashed-line';
                })
                .attr('d', d);
        });
    }

    /**
     * 点击事件，添加一个点,只有标绘时调用
     * @param latLng
     */
    addLatLng(latLng: LatLng, drawVertex: boolean = true) {
        this.latLngs[0].push(latLng);
        this.redraw();

        this.dashedLatlngs = [[latLng, latLng]];
        this.redrawDashedLine();

        if (drawVertex) {
            const vertexId = this.addVertex(latLng, [this], 'vertex');
            this.relateNodeIds[0].push(vertexId);
        }
    }

    drawStart() {
        this.drawing = true;
        // 添加第一个顶点
        const vertexId = this.addVertex(this.latLngs[0][0], [this], 'vertex');
        this.relateNodeIds[0].push(vertexId);
    }

    addVertex(latLng: LatLng, relateFeatures: Feature[], className: string, judgeRepeat = true): string {
        const show = true;
        const vertexId = this.featureOption.addVertex!(latLng, relateFeatures, { className }, judgeRepeat, show);
        return vertexId;
    }

    /**
     * 绘制中，鼠标移动事件
     * @param latLng
     */
    drawMove(latLng: LatLng) {
        this.dashedLatlngs[0][1] = latLng;
        this.redrawDashedLine();
        if (this.dashedVertex) {
            this.dashedVertex.changeLatlng(latLng, [0]);
        } else {
            const judgeRepeat = false;
            const vertexId = this.addVertex(this.dashedLatlngs[0][1], [], 'vertex dashed-vertex', judgeRepeat);

            this.dashedVertex = <Vertex>this.featureOption.nodesLayer?.features.get(vertexId);
        }
    }

    /**
     * 点击最后一个node,绘制结束
     * @returns
     */
    drawEnd() {
        const { nodesLayer } = this.featureOption;
        if (!nodesLayer) {
            return;
        }
        if (this.dashedVertex) {
            nodesLayer.remove(this.dashedVertex.id);
        }

        this.dashedVertex = undefined;
        this.drawing = false;

        if (this.latLngs[0].length === 1) {
            // 如果只插入了一个点就执行了结束，则删除这条线
            this.featureOption.remove!(this.type, this.id);
        } else {
            this.redraw();
            this.dashedLatlngs = [];
        }
        this.featureOption.drawEnd!(this);
    }

    changeLatlng(latLng: LatLng, indexs: number[], sequenceNum = 0): void {
        this.dashedLatlngs = [];
        indexs.forEach((item: number, i: number) => {
            if (item === -1) {
                return;
            }
            this.latLngs[sequenceNum].splice(item, 1, latLng);

            if (item === 0) {
                this.dashedLatlngs[i] = this.latLngs[sequenceNum].slice(0, 2);
            } else if (item === this.latLngs[sequenceNum].length - 1) {
                this.dashedLatlngs[i] = this.latLngs[sequenceNum].slice(-2);
            } else {
                this.dashedLatlngs[i] = this.latLngs[sequenceNum].slice(item - 1, item + 2);
            }
            this.redraw();
            this.redrawDashedLine();
        });
    }

    remove(): void {
        this.group.remove();
        this.latLngs = []; // 需要先把经纬度置空，删除点判断会用到
        const { nodesLayer } = this.featureOption;
        if (!nodesLayer) {
            return;
        }

        if (this.drawing) {
            // this.featureOption.drawEnd!(this);
            if (this.dashedVertex) {
                nodesLayer.remove(this.dashedVertex.id);
            }
        }
        if (this.relateNodeIds && this.relateNodeIds[0].length > 0) {
            this.relateNodeIds[0].forEach((nodeId: string, index: number) => {
                const node = <Vertex>nodesLayer.features.get(nodeId);
                // 如果顶点不存在，或者经纬度已经被清空，直接返回
                if (!node || node.latLngs.length === 0) {
                    return;
                }

                // 顶点存在多个关联要素,不能直接删除顶点
                const noRepeatNodeIds = new Set(node.relateFeatures.map((item) => item.id));
                if (noRepeatNodeIds.size > 1) {
                    node.relateFeatures = node.relateFeatures.filter((item) => {
                        return item.id !== this.id;
                    });
                    node.redraw();
                } else {
                    nodesLayer.remove(nodeId);
                }
            });
        }
    }

    isMinLatlngNum() {
        return this.latLngs[0].length === 2;
    }

    /**
     * 根据nodeId获取在relateNodeIds中的位置
     * @param nodeId
     * @returns
     */
    getNodeIndex(nodeId: string) {
        const sequence = 0;
        const indexs: number[] = [];
        // 自相交的线关联id会重复，需要遍历出来，不能用findIndex
        this.relateNodeIds[sequence]
            .map((id, index) => {
                return nodeId === id ? index : undefined;
            })
            .forEach((item) => {
                if (item !== undefined) {
                    indexs.push(item);
                }
            });
        return { sequence, indexs };
    }

    /**
     * 如果关联顶点被删除一个，并且处于绘制中，虚线需要重新生成
     */
    ajustDashLine() {
        this.drawing = true;
        const lastLatlng = JSON.parse(JSON.stringify(this.latLngs[0][this.latLngs.length - 1]));
        this.dashedLatlngs = [[lastLatlng]];
        // this.drawMove(lastLatlng);
    }

    /**
     * 改变中间顶点的可见性，非选中状态下，隐藏除了两端和交叉点之外的顶点
     */
    changeMidVertexVisible(visible: boolean) {
        for (const nodeId of this.relateNodeIds[0].slice(1, -1)) {
            this.featureOption.nodesLayer?.changeVertexVisible(nodeId, visible);
        }
    }

    /**
     * 添加线和面上的中间点
     */
    addMidPoint(position: { x: number; y: number }) {
        if (this.type !== 'line' && this.type !== 'polygon') {
            return;
        }

        const { addHistory, getCrossPoint } = this.featureOption;
        if (!getCrossPoint) {
            return;
        }
        // 添加基类
        addHistory(
            {
                annotation: getAnotation('add base '),
                features: [this],
            },
            true
        );

        // 交叉点和feature需要插入的点的index
        const cross = getCrossPoint(this, position);
        if (!cross) {
            return;
        }
        const vertexId = this.addVertex(cross.latLng, [this], 'vertex');
        this.latLngs[0].splice(cross.index, 0, cross.latLng);
        this.relateNodeIds[0].splice(cross.index, 0, vertexId);

        addHistory({
            annotation: getAnotation('add midpoint ', this),
            features: [this],
        });
    }

    /**
     * 经纬度转像素点坐标
     * @returns
     */
    toPoints(): Point[] {
        return this.latLngs[0].map((latLng) => {
            const pix = this.featureOption.transform.project(latLng);
            return new Point(pix[0], pix[1]);
        });
    }

    instance(): FeatureInstance {
        const attr: FeatureInstance = {
            ...this.json(),
            getInstanceType: this.getInstanceType.bind(this),
            changeId: this.changeId.bind(this),
            addMidPoint: this.addMidPoint.bind(this),
            setProperties: this.setProperties.bind(this),
            isEmpty: this.isEmpty.bind(this),
        };
        return deepClone(attr);
    }
}

export default Line;
